home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Special 16 / AMIGAplus Sonderheft 16 (1998)(ICP)(DE)[!].iso / pd / anwendungen / ispell-3.1.18src / defmt.c < prev    next >
C/C++ Source or Header  |  1995-01-23  |  24KB  |  905 lines

  1. #ifndef lint
  2. static char Rcs_Id[] =
  3.     "$Id: defmt.c,v 1.39 1995/01/08 23:23:54 geoff Exp $";
  4. #endif
  5.  
  6. /*
  7.  * defmt.c - Handle formatter constructs, mostly by scanning over them.
  8.  *
  9.  * This code originally resided in ispell.c, but was moved here to keep
  10.  * file sizes smaller.
  11.  *
  12.  * Copyright (c), 1983, by Pace Willisson
  13.  *
  14.  * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA
  15.  * All rights reserved.
  16.  *
  17.  * Redistribution and use in source and binary forms, with or without
  18.  * modification, are permitted provided that the following conditions
  19.  * are met:
  20.  *
  21.  * 1. Redistributions of source code must retain the above copyright
  22.  *    notice, this list of conditions and the following disclaimer.
  23.  * 2. Redistributions in binary form must reproduce the above copyright
  24.  *    notice, this list of conditions and the following disclaimer in the
  25.  *    documentation and/or other materials provided with the distribution.
  26.  * 3. All modifications to the source code must be clearly marked as
  27.  *    such.  Binary redistributions based on modified source code
  28.  *    must be clearly marked as modified versions in the documentation
  29.  *    and/or other materials provided with the distribution.
  30.  * 4. All advertising materials mentioning features or use of this software
  31.  *    must display the following acknowledgment:
  32.  *      This product includes software developed by Geoff Kuenning and
  33.  *      other unpaid contributors.
  34.  * 5. The name of Geoff Kuenning may not be used to endorse or promote
  35.  *    products derived from this software without specific prior
  36.  *    written permission.
  37.  *
  38.  * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
  39.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  40.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  41.  * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
  42.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  43.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  44.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  45.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  46.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  47.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  48.  * SUCH DAMAGE.
  49.  *
  50.  * The TeX code is originally by Greg Schaffer, with many improvements from
  51.  * Ken Stevens.  The nroff code is primarily from Pace Willisson, although
  52.  * other people have improved it.
  53.  */
  54.  
  55. /*
  56.  * $Log: defmt.c,v $
  57.  * Revision 1.39  1995/01/08  23:23:54  geoff
  58.  * Fix typos in a couple of comments.
  59.  *
  60.  * Revision 1.38  1995/01/03  19:24:14  geoff
  61.  * Add code to handle the LaTeX \verb command.
  62.  *
  63.  * Revision 1.37  1994/12/27  23:08:54  geoff
  64.  * Fix a bug in TeX backslash processing that caused ispell to become
  65.  * confused when it encountered an optional argument to a
  66.  * double-backslash command.  Be a little smarter about scanning for
  67.  * curly-brace matches, so that we avoid missing a math-mode transition
  68.  * during the scan.
  69.  *
  70.  * Revision 1.36  1994/10/25  05:46:34  geoff
  71.  * Recognize a few more Latex commands: pagestyle, pagenumbering,
  72.  * setcounter, addtocounter, setlength, addtolength, settowidth.
  73.  *
  74.  * Revision 1.35  1994/10/18  04:03:19  geoff
  75.  * Add code to skip hex numbers if they're preceded by '0x'.
  76.  *
  77.  * Revision 1.34  1994/10/04  03:51:24  geoff
  78.  * Modify the parsing so that TeX commands are ignored even within
  79.  * comments, but do not affect the overall parsing state.  (This is
  80.  * slightly imperfect, in that some types of modality are ignored when
  81.  * comments are entered.  But it should solve nearly all the problems
  82.  * with commented-out TeX commands.)  This also fixes a couple of minor
  83.  * bugs with TeX deformatting.
  84.  *
  85.  * Revision 1.33  1994/10/03  17:06:07  geoff
  86.  * Remember to use contextoffset when reporting complete misses
  87.  *
  88.  * Revision 1.32  1994/08/31  05:58:41  geoff
  89.  * Report the offset-within-line correctly in -a mode even if the line is
  90.  * longer than BUFSIZ characters.
  91.  *
  92.  * Revision 1.31  1994/05/25  04:29:28  geoff
  93.  * If two boundary characters appear in a row, consider it the end of the
  94.  * word.
  95.  *
  96.  * Revision 1.30  1994/05/17  06:44:08  geoff
  97.  * Add the new argument to all calls to good and compoundgood.
  98.  *
  99.  * Revision 1.29  1994/03/16  06:30:41  geoff
  100.  * Don't lose track of math mode when an array environment is embedded.
  101.  *
  102.  * Revision 1.28  1994/03/15  05:31:57  geoff
  103.  * Add TeX_strncmp, which allows us to handle AMS-TeX constructs like
  104.  * \endroster without getting confused.
  105.  *
  106.  * Revision 1.27  1994/02/14  00:34:53  geoff
  107.  * Pass length arguments to correct().
  108.  *
  109.  * Revision 1.26  1994/01/25  07:11:25  geoff
  110.  * Get rid of all old RCS log lines in preparation for the 3.1 release.
  111.  *
  112.  */
  113.  
  114. #include <ctype.h>
  115. #include "config.h"
  116. #include "ispell.h"
  117. #include "proto.h"
  118. #include "msgs.h"
  119.  
  120. static char *    skiptoword P ((char * bufp));
  121. char *        skipoverword P ((char * bufp));
  122. void        checkline P ((FILE * ofile));
  123. static int    TeX_math_end P ((char ** bufp));
  124. static int    TeX_math_begin P ((char ** bufp));
  125. static int    TeX_LR_begin P ((char ** bufp));
  126. static int    TeX_LR_check P ((int begin_p, char ** bufp));
  127. static void    TeX_skip_args P ((char ** bufp));
  128. static int    TeX_math_check P ((int cont_char, char ** bufp));
  129. static void    TeX_skip_parens P ((char ** bufp));
  130. static void    TeX_open_paren P ((char ** bufp));
  131. static void    TeX_skip_check P ((char ** bufp));
  132. static int    TeX_strncmp P ((char * a, char * b, int n));
  133.  
  134. #define ISTEXTERM(c)   (((c) == TEXLEFTCURLY) || \
  135.             ((c) == TEXRIGHTCURLY) || \
  136.             ((c) == TEXLEFTSQUARE) || \
  137.             ((c) == TEXRIGHTSQUARE))
  138. #define ISMATHCH(c)    (((c) == TEXBACKSLASH) || \
  139.             ((c) == TEXDOLLAR) || \
  140.             ((c) == TEXPERCENT))
  141.  
  142. static int        TeX_comment = 0;
  143.  
  144. /*
  145.  * The following variables are used to save the parsing state when
  146.  * processing comments.  This allows comments to be parsed without
  147.  * affecting the overall nesting.
  148.  */
  149.  
  150. static int save_math_mode;
  151. static char save_LaTeX_Mode;
  152.  
  153. static char * skiptoword (bufp)        /* Skip to beginning of a word */
  154.     char *    bufp;
  155.     {
  156.  
  157.     while (*bufp
  158.       &&  ((!isstringch (bufp, 0)  &&  !iswordch (chartoichar (*bufp)))
  159.     ||  isboundarych (chartoichar (*bufp))
  160.     ||  (tflag  &&  (math_mode & 1)))
  161.       )
  162.     {
  163.     /* check paren necessity... */
  164.     if (tflag) /* TeX or LaTeX stuff */
  165.         {
  166.         /* Odd numbers mean we are in "math mode" */
  167.         /* Even numbers mean we are in LR or */
  168.         /* paragraph mode */
  169.         if (*bufp == TEXPERCENT)
  170.         {
  171.         if (!TeX_comment)
  172.             {
  173.             save_math_mode = math_mode;
  174.             save_LaTeX_Mode = LaTeX_Mode;
  175.             math_mode = 0;
  176.             LaTeX_Mode = 'P';
  177.             TeX_comment = 1;
  178.             }
  179.         }
  180.         else if (math_mode & 1)
  181.         {
  182.         if ((LaTeX_Mode == 'e'  &&  TeX_math_check('e', &bufp))
  183.           || (LaTeX_Mode == 'm'  &&  TeX_LR_check(1, &bufp)))
  184.             math_mode--;    /* end math mode */
  185.         else
  186.             {
  187.             while (*bufp  && !ISMATHCH(*bufp))
  188.             bufp++;
  189.             if (*bufp == 0)
  190.             break;
  191.             if (TeX_math_end(&bufp))
  192.             math_mode--;
  193.             }
  194.         if (math_mode < 0)
  195.             {
  196.             (void) fprintf (stderr,
  197.              DEFMT_C_TEX_MATH_ERROR);
  198.             math_mode = 0;
  199.             }
  200.         }
  201.         else
  202.         {
  203.         if (math_mode > 1
  204.           &&  *bufp == TEXRIGHTCURLY
  205.           &&  (math_mode < (math_mode & 127) * 128))
  206.             math_mode--;    /* re-enter math */
  207.         else if (LaTeX_Mode == 'm'
  208.             || (math_mode && (math_mode >= (math_mode & 127) * 128)
  209.           &&  (TeX_strncmp(bufp, "\\end", 4)
  210.             == 0)))
  211.             {
  212.             if (TeX_LR_check(0, &bufp))
  213.             math_mode--;
  214.             }
  215.         else if (LaTeX_Mode == 'b'  &&  TeX_math_check('b', &bufp))
  216.             {
  217.             /* continued begin */
  218.             math_mode++;
  219.             }
  220.         else if (LaTeX_Mode == 'r')
  221.             {
  222.             /* continued "reference" */
  223.             TeX_skip_parens(&bufp);
  224.             LaTeX_Mode = 'P';
  225.             }
  226.         else if (TeX_math_begin(&bufp))
  227.             /* checks references and */
  228.             /* skips \ commands */
  229.             math_mode++;
  230.         }
  231.         if (*bufp == 0)
  232.         break;
  233.         }
  234.     else            /* formatting escape sequences */
  235.         {
  236.         if (*bufp == NRBACKSLASH)
  237.         {
  238.         switch ( bufp[1] )
  239.             {
  240.             case 'f':
  241.             if(bufp[2] == NRLEFTPAREN)
  242.                 {
  243.                 /* font change: \f(XY */
  244.                 bufp += 5;
  245.                 }
  246.             else
  247.                 {
  248.                 /* ) */
  249.                 /* font change: \fX */
  250.                 bufp += 3;
  251.                 }
  252.             continue;
  253.             case 's':
  254.             /* size change */
  255.             bufp += 2;
  256.             if (*bufp == '+'  ||  *bufp == '-')
  257.                 bufp++;
  258.             /* This looks wierd 'cause we
  259.             ** assume *bufp is now a digit.
  260.             */
  261.             bufp++;
  262.             if (isdigit (*bufp))
  263.                 bufp++;
  264.             continue;
  265.             default:
  266.             if (bufp[1] == NRLEFTPAREN)
  267.                 {
  268.                 /* extended char set */
  269.                 /* escape:  \(XX */
  270.                 /* ) */
  271.                 bufp += 4;
  272.                 continue;
  273.                 }
  274.             else if (bufp[1] == NRSTAR)
  275.                 {
  276.                 if (bufp[2] == NRLEFTPAREN)
  277.                 bufp += 5;
  278.                 else
  279.                 bufp += 3;
  280.                 continue;
  281.                 }
  282.             break;
  283.             }
  284.         }
  285.         }
  286.     /*
  287.      * Skip hex numbers, but not if we're in non-terse askmode.
  288.      * (In that case, we'd lose sync if we skipped hex.)
  289.      */
  290.     if (*bufp == '0'
  291.       &&  (bufp[1] == 'x'  ||  bufp[1] == 'X')
  292.       &&  (terse  ||  !aflag))
  293.         {
  294.         bufp += 2;
  295.         while (isxdigit (*bufp))
  296.         bufp++;
  297.         }
  298.     else
  299.         bufp++;
  300.     }
  301.     if (*bufp == '\0')
  302.     {
  303.     if (TeX_comment)
  304.         {
  305.         math_mode = save_math_mode;
  306.         LaTeX_Mode = save_LaTeX_Mode;
  307.         TeX_comment = 0;
  308.         }
  309.     }
  310.     return bufp;
  311.     }
  312.  
  313. char * skipoverword (bufp)    /* Return pointer to end of a word */
  314.     register char *    bufp;    /* Start of word -- MUST BE A REAL START */
  315.     {
  316.     register char *    lastboundary;
  317.     register int    scharlen; /* Length of a string character */
  318.  
  319.     lastboundary = NULL;
  320.     for (  ;  ;  )
  321.     {
  322.     if (*bufp == '\0')
  323.         {
  324.         if (TeX_comment)
  325.         {
  326.         math_mode = save_math_mode;
  327.         LaTeX_Mode = save_LaTeX_Mode;
  328.         TeX_comment = 0;
  329.         }
  330.         break;
  331.         }
  332.     else if (l_isstringch(bufp, scharlen, 0))
  333.         {
  334.         bufp += scharlen;
  335.         lastboundary = NULL;
  336.         }
  337.     /*
  338.     ** Note that we get here if a character satisfies
  339.     ** isstringstart() but isn't in the string table;  this
  340.     ** allows string characters to start with word characters.
  341.     */
  342.     else if (iswordch (chartoichar (*bufp)))
  343.         {
  344.         bufp++;
  345.         lastboundary = NULL;
  346.         }
  347.     else if (isboundarych (chartoichar (*bufp)))
  348.         {
  349.         if (lastboundary == NULL)
  350.         lastboundary = bufp;
  351.         else if (lastboundary == bufp - 1)
  352.         break;            /* Double boundary -- end of word */
  353.         bufp++;
  354.         }
  355.     else
  356.         break;            /* End of the word */
  357.     }
  358.     /*
  359.     ** If the word ended in one or more boundary characters, 
  360.     ** the address of the first of these is in lastboundary, and it
  361.     ** is the end of the word.  Otherwise, bufp is the end.
  362.     */
  363.     return (lastboundary != NULL) ? lastboundary : bufp;
  364.     }
  365.  
  366. void checkline (ofile)
  367.     FILE *        ofile;
  368.     {
  369.     register char *    p;
  370.     register char *    endp;
  371.     int            hadlf;
  372.     register int    len;
  373.     register int    i;
  374.     int            ilen;
  375.  
  376.     currentchar = contextbufs[0];
  377.     len = strlen (contextbufs[0]) - 1;
  378.     hadlf = contextbufs[0][len] == '\n';
  379.     if (hadlf)
  380.     contextbufs[0][len] = 0;
  381.  
  382.     if (!tflag)
  383.     {
  384.     /* skip over .if */
  385.     if (*currentchar == NRDOT
  386.       &&  (strncmp (currentchar + 1, "if t", 4) == 0
  387.         ||  strncmp (currentchar + 1, "if n", 4) == 0))
  388.         {
  389.         copyout (¤tchar,5);
  390.         while (*currentchar
  391.           &&  myspace (chartoichar (*currentchar)))
  392.         copyout (¤tchar, 1);
  393.         }
  394.  
  395.     /* skip over .ds XX or .nr XX */
  396.     if (*currentchar == NRDOT
  397.       &&  (strncmp (currentchar + 1, "ds ", 3) == 0 
  398.         ||  strncmp (currentchar + 1, "de ", 3) == 0
  399.         ||  strncmp (currentchar + 1, "nr ", 3) == 0))
  400.         {
  401.         copyout (¤tchar, 4);
  402.         while (*currentchar
  403.           &&  myspace (chartoichar (*currentchar)))
  404.         copyout(¤tchar, 1);
  405.         while (*currentchar
  406.           &&  !myspace (chartoichar (*currentchar)))
  407.         copyout(¤tchar, 1);
  408.         if (*currentchar == 0)
  409.         {
  410.         if (!lflag  &&  (aflag  ||  hadlf))
  411.             (void) putc ('\n', ofile);
  412.         return;
  413.         }
  414.         }
  415.     }
  416.  
  417.  
  418.     /* if this is a formatter command, skip over it */
  419.     if (!tflag && *currentchar == NRDOT)
  420.     {
  421.     while (*currentchar  &&  !myspace (chartoichar (*currentchar)))
  422.         {
  423.         if (!aflag && !lflag)
  424.         (void) putc (*currentchar, ofile);
  425.         currentchar++;
  426.         }
  427.     if (*currentchar == 0)
  428.         {
  429.         if (!lflag  &&  (aflag  ||  hadlf))
  430.         (void) putc ('\n', ofile);
  431.         return;
  432.         }
  433.     }
  434.  
  435.     for (  ;  ;  )
  436.     {
  437.     p = skiptoword (currentchar);
  438.     if (p != currentchar)
  439.         copyout (¤tchar, p - currentchar);
  440.  
  441.     if (*currentchar == 0)
  442.         break;
  443.  
  444.     p = ctoken;
  445.     endp = skipoverword (currentchar);
  446.     while (currentchar < endp  &&  p < ctoken + sizeof ctoken - 1)
  447.         *p++ = *currentchar++;
  448.     *p = 0;
  449.     if (strtoichar (itoken, ctoken, INPUTWORDLEN * sizeof (ichar_t), 0))
  450.         (void) fprintf (stderr, WORD_TOO_LONG (ctoken));
  451.     ilen = icharlen (itoken);
  452.  
  453.     if (lflag)
  454.         {
  455.         if (ilen > minword
  456.           &&  !good (itoken, 0, 0, 0, 0)
  457.           &&  !cflag  &&  !compoundgood (itoken, 0))
  458.         (void) fprintf (ofile, "%s\n", ctoken);
  459.         }
  460.     else
  461.         {
  462.         if (aflag)
  463.         {
  464.         if (ilen <= minword)
  465.             {
  466.             /* matched because of minword */
  467.             if (!terse)
  468.             (void) fprintf (ofile, "*\n");
  469.             continue;
  470.             }
  471.         if (good (itoken, 0, 0, 0, 0))
  472.             {
  473.             if (hits[0].prefix == NULL
  474.               &&  hits[0].suffix == NULL)
  475.             {
  476.             /* perfect match */
  477.             if (!terse)
  478.                 (void) fprintf (ofile, "*\n");
  479.             }
  480.             else if (!terse)
  481.             {
  482.             /* matched because of root */
  483.             (void) fprintf (ofile, "+ %s\n",
  484.               hits[0].dictent->word);
  485.             }
  486.             }
  487.         else if (compoundgood (itoken, 0))
  488.             {
  489.             /* compound-word match */
  490.             if (!terse)
  491.             (void) fprintf (ofile, "-\n");
  492.             }
  493.         else
  494.             {
  495.             makepossibilities (itoken);
  496.             if (pcount)
  497.             {
  498.             /*
  499.             ** print &  or ?, ctoken, then
  500.             ** character offset, possibility
  501.             ** count, and the possibilities.
  502.             */
  503.             (void) fprintf (ofile, "%c %s %d %d",
  504.               easypossibilities ? '&' : '?',
  505.               ctoken,
  506.               easypossibilities,
  507.               (int) ((currentchar - contextbufs[0])
  508.                 - strlen (ctoken)) + contextoffset);
  509.             for (i = 0;  i < MAXPOSSIBLE;  i++)
  510.                 {
  511.                 if (possibilities[i][0] == 0)
  512.                 break;
  513.                 (void) fprintf (ofile, "%c %s",
  514.                   i ? ',' : ':', possibilities[i]);
  515.                 }
  516.             (void) fprintf (ofile, "\n");
  517.             }
  518.             else
  519.             {
  520.             /*
  521.             ** No possibilities found for word TOKEN
  522.             */
  523.             (void) fprintf (ofile, "# %s %d\n",
  524.               ctoken,
  525.               (int) ((currentchar - contextbufs[0])
  526.                 - strlen (ctoken)) + contextoffset);
  527.             }
  528.             }
  529.         }
  530.         else
  531.         {
  532.         if (!quit)
  533.            correct (ctoken, sizeof ctoken, itoken, sizeof itoken,
  534.              ¤tchar);
  535.         }
  536.         }
  537.     if (!aflag  &&  !lflag)
  538.        (void) fprintf (ofile, "%s", ctoken);
  539.     }
  540.  
  541.     if (!lflag  &&  (aflag  ||  hadlf))
  542.        (void) putc ('\n', ofile);
  543.    }
  544.  
  545. /* must check for \begin{mbox} or whatever makes new text region. */
  546. static int TeX_math_end (bufp)
  547.     char **    bufp;
  548.     {
  549.  
  550.     if (**bufp == TEXDOLLAR)
  551.     {
  552.     if ((*bufp)[1] == TEXDOLLAR)
  553.         (*bufp)++;
  554.     return 1;
  555.     }
  556.     else if (**bufp == TEXPERCENT)
  557.     {
  558.     if (!TeX_comment)
  559.         {
  560.         save_math_mode = math_mode;
  561.         save_LaTeX_Mode = LaTeX_Mode;
  562.         math_mode = 0;
  563.         LaTeX_Mode = 'P';
  564.         TeX_comment = 1;
  565.         }
  566.     return 0;
  567.     }
  568.     /* processing extended TeX command */
  569.     (*bufp)++;
  570.     if (**bufp == TEXRIGHTPAREN  ||  **bufp == TEXRIGHTSQUARE)
  571.     return 1;
  572.     if (TeX_LR_begin (bufp))    /* check for switch back to LR mode */
  573.     return 1;
  574.     if (TeX_strncmp (*bufp, "end", 3) == 0)
  575.     /* find environment that is ending */
  576.     return TeX_math_check ('e', bufp);
  577.     else
  578.     return 0;
  579.     }
  580.  
  581. static int TeX_math_begin (bufp)
  582.     char **    bufp;
  583.     {
  584.  
  585.     if (**bufp == TEXDOLLAR)
  586.     {
  587.     if ((*bufp)[1] == TEXDOLLAR)
  588.         (*bufp)++;
  589.     return 1;
  590.     }
  591.     while (**bufp == TEXBACKSLASH)
  592.     {
  593.     (*bufp)++; /* check for null char here? */
  594.     if (**bufp == TEXBACKSLASH)
  595.         {
  596.         (*bufp)++;
  597.         continue;
  598.         }
  599.     if (**bufp == TEXLEFTPAREN  ||  **bufp == TEXLEFTSQUARE)
  600.         return 1;
  601.     if (TeX_strncmp (*bufp, "begin", 5) == 0)
  602.         {
  603.         if (TeX_math_check ('b', bufp))
  604.         return 1;
  605.         else
  606.         (*bufp)--;
  607.         }
  608.     else
  609.         {
  610.         TeX_skip_check (bufp);
  611.         return 0;
  612.         }
  613.     }
  614.       /*
  615.        * Ignore references for the tib (1) bibliography system, that
  616.        * is, text between a ``[.'' or ``<.'' and ``.]'' or ``.>''.
  617.        * We don't care whether they match, tib doesn't care either.
  618.        *
  619.        * A limitation is that the entire tib reference must be on one
  620.        * line, or we break down and check the remainder anyway.
  621.        */ 
  622.     if ((**bufp == TEXLEFTSQUARE  ||  **bufp == TEXLEFTANGLE)
  623.       &&  (*bufp)[1] == TEXDOT)
  624.     {
  625.     (*bufp)++;
  626.     while (**bufp)
  627.         {
  628.         if (*(*bufp)++ == TEXDOT
  629.           &&  (**bufp == TEXRIGHTSQUARE  ||  **bufp == TEXRIGHTANGLE))
  630.         return TeX_math_begin (bufp);
  631.         }
  632.     return 0;
  633.     }
  634.     else
  635.     return 0;
  636.     }
  637.  
  638. static int TeX_LR_begin (bufp)
  639.     char **    bufp;
  640.     {
  641.  
  642.     if ((TeX_strncmp (*bufp, "mbox", 4) == 0)
  643.       ||  (TeX_strncmp (*bufp, "makebox", 7) == 0)
  644.       ||  (TeX_strncmp (*bufp, "fbox", 4) == 0)
  645.       || (TeX_strncmp (*bufp, "framebox", 8) == 0))
  646.     math_mode += 2;
  647.     else if ((TeX_strncmp(*bufp, "parbox", 6) == 0)
  648.       || (TeX_strncmp(*bufp, "raisebox", 8) == 0))
  649.     {
  650.     math_mode += 2;
  651.     TeX_open_paren (bufp);
  652.     if (**bufp)
  653.         (*bufp)++;
  654.     else
  655.         LaTeX_Mode = 'r'; /* same as reference -- skip {} */
  656.     }
  657.     else if (TeX_strncmp(*bufp, "begin", 5) == 0)
  658.     return TeX_LR_check (1, bufp);    /* minipage */
  659.     else
  660.     return 0;
  661.  
  662.     /* skip tex command name and optional or width arguments. */
  663.     TeX_open_paren (bufp);
  664.     return 1;
  665.     }
  666.  
  667. static int TeX_LR_check (begin_p, bufp)
  668.     int        begin_p;
  669.     char **    bufp;
  670.     {
  671.  
  672.     TeX_open_paren (bufp);
  673.     if (**bufp == 0)    /* { */
  674.     {
  675.     LaTeX_Mode = 'm';
  676.     return 0;    /* remain in math mode until '}' encountered. */
  677.     }
  678.     else
  679.     LaTeX_Mode = 'P';
  680.     if (strncmp (++(*bufp), "minipage", 8) == 0)
  681.     {
  682.     TeX_skip_parens (bufp);
  683.     if (**bufp)
  684.         (*bufp)++;
  685.     if (begin_p)
  686.         {
  687.         TeX_skip_parens (bufp); /* now skip opt. args if on this line. */
  688.         math_mode += 2;
  689.         /* indicate minipage mode. */
  690.         math_mode += ((math_mode & 127) - 1) * 128;
  691.         }
  692.     else
  693.         {
  694.         math_mode -= (math_mode & 127) * 128;
  695.         if (math_mode < 0)
  696.         {
  697.         (void) fprintf (stderr, DEFMT_C_LR_MATH_ERROR);
  698.         math_mode = 1;
  699.         }
  700.         }
  701.     return 1;
  702.     }
  703.     (*bufp)--;
  704.     return 0;
  705.     }
  706.  
  707. /* Skips the begin{ARG}, and optionally up to two {PARAM}{PARAM}'s to
  708.  *  the begin if they are required.  However, Only skips if on this line.
  709.  */
  710. static void TeX_skip_args (bufp)
  711.     char **    bufp;
  712.     {
  713.     register int skip_cnt = 0; /* Max of 2. */
  714.  
  715.     if (strncmp(*bufp, "tabular", 7) == 0
  716.       ||  strncmp(*bufp, "minipage", 8) == 0)
  717.     skip_cnt++;
  718.     if (strncmp(*bufp, "tabular*", 8) == 0)
  719.     skip_cnt++;
  720.     TeX_skip_parens (bufp);    /* Skip to the end of the \begin{} parens */
  721.     if (**bufp)
  722.     (*bufp)++;
  723.     else
  724.     return;
  725.     if (skip_cnt--)
  726.     TeX_skip_parens (bufp);    /* skip 1st {PARAM}. */
  727.     else
  728.     return;
  729.     if (**bufp)
  730.     (*bufp)++;
  731.     else
  732.     return;
  733.     if (skip_cnt)
  734.     TeX_skip_parens (bufp);    /* skip to end of 2nd {PARAM}. */
  735.     }
  736.  
  737. static int TeX_math_check (cont_char, bufp)
  738.     int        cont_char;
  739.     char **    bufp;
  740.     {
  741.  
  742.     TeX_open_paren (bufp);
  743.     /* Check for end of line, continue later. */
  744.     if (**bufp == 0)
  745.     {
  746.     LaTeX_Mode = (char) cont_char;
  747.     return 0;
  748.     }
  749.     else
  750.     LaTeX_Mode = 'P';
  751.  
  752.     if (strncmp (++(*bufp), "equation", 8) == 0
  753.       ||  strncmp (*bufp, "eqnarray", 8) == 0
  754.       ||  strncmp (*bufp, "displaymath", 11) == 0
  755.       ||  strncmp (*bufp, "picture", 7) == 0
  756. #ifdef IGNOREBIB
  757.       ||  strncmp (*bufp, "thebibliography", 15) == 0
  758. #endif
  759.       ||  strncmp (*bufp, "math", 4) == 0)
  760.     {
  761.     (*bufp)--;
  762.     TeX_skip_parens (bufp);
  763.     return 1;
  764.     }
  765.     if (cont_char == 'b')
  766.     TeX_skip_args (bufp);
  767.     else
  768.     TeX_skip_parens (bufp);
  769.     return 0;
  770.     }
  771.  
  772. static void TeX_skip_parens (bufp)
  773.     char **    bufp;
  774.     {
  775.  
  776.     while (**bufp  &&  **bufp != TEXRIGHTCURLY  &&  **bufp != TEXDOLLAR)
  777.     (*bufp)++;
  778.     }
  779.  
  780. static void TeX_open_paren (bufp)
  781.     char **    bufp;
  782.     {
  783.     while (**bufp  &&  **bufp != TEXLEFTCURLY  &&  **bufp != TEXDOLLAR)
  784.     (*bufp)++;
  785.     }
  786.  
  787. static void TeX_skip_check (bufp)
  788.     char **    bufp;
  789.     {
  790.     int        charlen;
  791.     int        skip_ch;
  792.  
  793.     /* ADDITIONALLY, MAY WANT TO ADD:
  794.      * documentstyle, pagestyle, pagenumbering
  795.      * WITH TWO {} {}'S TO SKIP:
  796.      * setcounter, addtocounter,
  797.      * setlength, addtolength, settowidth
  798.      */
  799.  
  800.     if (TeX_strncmp(*bufp, "end", 3) == 0
  801.       ||  TeX_strncmp(*bufp, "vspace", 6) == 0
  802.       ||  TeX_strncmp(*bufp, "hspace", 6) == 0
  803.       ||  TeX_strncmp(*bufp, "cite", 4) == 0
  804.       ||  TeX_strncmp(*bufp, "ref", 3) == 0
  805.       ||  TeX_strncmp(*bufp, "parbox", 6) == 0
  806.       ||  TeX_strncmp(*bufp, "label", 5) == 0
  807.       ||  TeX_strncmp(*bufp, "input", 5) == 0
  808.       ||  TeX_strncmp(*bufp, "nocite", 6) == 0
  809.       ||  TeX_strncmp(*bufp, "include", 7) == 0
  810.       ||  TeX_strncmp(*bufp, "includeonly", 11) == 0
  811.       ||  TeX_strncmp(*bufp, "documentstyle", 13) == 0
  812.       ||  TeX_strncmp(*bufp, "pagestyle", 9) == 0
  813.       ||  TeX_strncmp(*bufp, "pagenumbering", 13) == 0
  814. #ifndef IGNOREBIB
  815.       ||  TeX_strncmp(*bufp, "bibliography", 12) == 0
  816.       ||  TeX_strncmp(*bufp, "bibitem", 7) == 0
  817. #endif
  818.       ||  TeX_strncmp(*bufp, "hyphenation", 11) == 0
  819.       ||  TeX_strncmp(*bufp, "pageref", 7) == 0)
  820.     {
  821.     TeX_skip_parens (bufp);
  822.     if (**bufp == 0)
  823.         LaTeX_Mode = 'r';
  824.     }
  825.     else if (TeX_strncmp(*bufp, "rule", 4) == 0        /* skip two args. */
  826.       ||  TeX_strncmp(*bufp, "setcounter", 10) == 0
  827.       ||  TeX_strncmp(*bufp, "addtocounter", 12) == 0
  828.       ||  TeX_strncmp(*bufp, "setlength", 9) == 0
  829.       ||  TeX_strncmp(*bufp, "addtolength", 11) == 0
  830.       ||  TeX_strncmp(*bufp, "settowidth", 10) == 0)
  831.     {
  832.     TeX_skip_parens (bufp);
  833.     if (**bufp == 0)    /* Only skips one {} if not on same line. */
  834.         LaTeX_Mode = 'r';
  835.     else            /* Skip second arg. */
  836.         {
  837.         (*bufp)++;
  838.         TeX_skip_parens (bufp);
  839.         if (**bufp == 0)
  840.         LaTeX_Mode = 'r';
  841.         }
  842.     }
  843.     else if (TeX_strncmp (*bufp, "verb", 4) == 0)
  844.     {
  845.     skip_ch = (*bufp)[4];
  846.     *bufp += 5;
  847.     while (**bufp != skip_ch  &&  **bufp != '\0')
  848.         (*bufp)++;
  849.     }
  850.     else
  851.     {
  852.     /* Optional tex arguments sometimes should and
  853.     ** sometimes shouldn't be checked
  854.     ** (eg \section [C Programming] {foo} vs
  855.     **     \rule [3em] {0.015in} {5em})
  856.     ** SO -- we'll always spell-check it rather than make a
  857.     ** full LaTeX parser.
  858.     */
  859.  
  860.     /* Must look at the space after the command. */
  861.     while (**bufp
  862.       && (l1_isstringch (*bufp, charlen, 0)
  863.         ||  iswordch (chartoichar (**bufp))))
  864.         {
  865.         if (!isstringch (*bufp + charlen, 0)
  866.           &&  !iswordch (chartoichar ((*bufp)[charlen])))
  867.         break;
  868.         *bufp += charlen;
  869.         }
  870.     /*
  871.     ** Our caller expects to skip over a single character.  So we
  872.     ** need to back up by one.  Ugh.
  873.     */
  874.     (*bufp)--;
  875.     }
  876.     }
  877.  
  878. /*
  879.  * TeX_strncmp is like strncmp, except that it returns inequality if
  880.  * the following character of a is alphabetic.  We do not use
  881.  * iswordch here because TeX itself won't normally accept
  882.  * nonalphabetics (except maybe on ISO Latin-1 installations?  I'll
  883.  * have to look into that).  As a special hack, because LaTeX uses the
  884.  * @ sign so much, we'll also accept that character.
  885.  *
  886.  * Properly speaking, the @ sign should be settable in the hash file
  887.  * header, but I doubt that it varies, and I don't want to change the
  888.  * syntax of affix files right now.
  889.  */
  890. static int TeX_strncmp (a, b, n)
  891.     char *    a;        /* Strings to compare */
  892.     char *    b;        /* ... */
  893.     int        n;        /* Number of characters to compare */
  894.     {
  895.     int        cmpresult;    /* Result of calling strncmp */
  896.  
  897.     cmpresult = strncmp (a, b, n);
  898.     if (cmpresult == 0)
  899.     {
  900.     if (isascii (a[n])  &&  isalpha (a[n]))
  901.         return 1;        /* Force inequality if alpha follows */
  902.     }
  903.     return cmpresult;
  904.     }
  905.